import 'dart:async';

import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
// import 'package:lib_rtc/lib_rtc.dart';
// import 'package:lib_runtime/lib_runtime.dart';

import '../../lib_runtime/global_config.dart';
import '../../lib_runtime/logger.dart';

class RtcManager {
  static String get appId => GlobalConfig.isOfficial
      ? '86b2ac3562e343f5af1acd303e188c6c'
      : '86b2ac3562e343f5af1acd303e188c6c';

  static final RtcManager instance = RtcManager._();

  late RtcEngine _engine;

  // late MusicContentCenter _musicContentCenter;

  RtcEngineEventHandler? _handler;

  ClientRoleType? role;

  /// 是否初始化
  bool _inited = false;

  /// 是否加入频道
  bool _joined = false;

  bool _lowLatency = false;

  int _musicPlayerId = 0;

  RtcManager._();

  // MusicContentCenter get musicContentCenter => _musicContentCenter;

  RtcEngine get rtcEngine => _engine;

  set musicPlayerId(int id) {
    _musicPlayerId = id;
    updateMediaOptions(ChannelMediaOptions(
      publishMediaPlayerId: musicPlayerId,
      publishMediaPlayerAudioTrack: true,
    ));
  }

  int get musicPlayerId => _musicPlayerId;

  Future<void> init(String? rtcToken, int id, bool lowLatency,
      ValueChanged<List<AudioVolumeInfo>> onVolumeInfo) async {
    log('RtcManager.init with rtmToken: $rtcToken, id: $id, _inited: $_inited, appId: $appId');

    if (_inited ||
        appId.isEmpty ||
        id <= 0 /*|| (rtmToken?.isEmpty ?? true) */) {
      log('RtcManager.init ignore!!!');
      return;
    }

    _lowLatency = lowLatency;

    await _initEngine(onVolumeInfo);
    // await _initMusicContentCenter(rtmToken!, id);

    log('RtcManager init done ${id}');
    _inited = true;
  }

  Future<void> _initEngine(
      ValueChanged<List<AudioVolumeInfo>> onVolumeInfo) async {
    _engine = createAgoraRtcEngine();

    // 初始化 RtcEngine
    await _engine.initialize(RtcEngineContext(
        appId: appId,
        logConfig:
            const LogConfig(fileSizeInKB: 1024, level: LogLevel.logLevelError),
        channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
        audioScenario: AudioScenarioType.audioScenarioGameStreaming));

    _initEventHandler(onVolumeInfo);

    _engine.enableAudioVolumeIndication(
        interval: 400, smooth: 3, reportVad: true);

    // 开启音频
    await _engine.enableAudio();

    // scenario设置为game streaming，使用媒体音量播放
    await _engine.setAudioProfile(
        profile: AudioProfileType.audioProfileMusicHighQuality,
        scenario: AudioScenarioType.audioScenarioGameStreaming);

    // 蓝牙耳机走a2dp协议，使用媒体音量播放
    await _engine.setParameters('{\"che.audio.force_bluetooth_a2dp\":true}');

    // 打开agc，自动增益
    await _engine.setParameters('{\"che.audio.agc.enable\":true}');

    // 关闭opensl
    await _engine.setParameters('{\"che.audio.opensl\":false}');
  }

  void _initEventHandler(ValueChanged<List<AudioVolumeInfo>> onVolumeInfo) {
    _handler = RtcEngineEventHandler(
      onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
        /// 加入频道回调
        log('RtcManager local user ${connection.localUid} joined');
      },
      onPermissionError: (PermissionType permissionType) {
        log('RtcManager onPermissionError $permissionType ');
      },
      onEncryptionError:
          (RtcConnection connection, EncryptionErrorType errorType) {
        log('RtcManager onEncryptionError errorType = $errorType');
      },
      onStreamMessageError: (RtcConnection connection, int remoteUid,
          int streamId, ErrorCodeType code, int missed, int cached) {
        log('RtcManager onStreamMessageError remoteUid = $remoteUid, code = $code');
      },
      onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
        /// 用户加入频道回调
        log('RtcManager remote user $remoteUid joined');
      },
      onUserOffline: (RtcConnection connection, int remoteUid,
          UserOfflineReasonType reason) {
        /// 用户离开频道
        log('RtcManager remote user $remoteUid left channel');

        /// 设置对应用户下麦操作 ?
      },
      onAudioMixingStateChanged:
          (AudioMixingStateType state, AudioMixingReasonType reason) {
        log('AudioMixingStateType $state AudioMixingReasonType reason');
      },
      onAudioVolumeIndication: (RtcConnection connection,
          List<AudioVolumeInfo> speakers, int speakerNumber, int totalVolume) {
        // log('onAudioVolumeIndication speakerNumber: $speakerNumber, totalVolume: $totalVolume, speakers: [${speakers.map((_) => '${_.uid}: ${_.volume}')}]');
        onVolumeInfo.call(speakers);
      },
      onConnectionStateChanged: (RtcConnection connection,
          ConnectionStateType state, ConnectionChangedReasonType reason) {
        log('onConnectionStateChanged $state');
      },
      onError: (ErrorCodeType err, String msg) {
        log('onError: $err, detail: $msg');
      },
      onAudioDeviceStateChanged: (String deviceId, MediaDeviceType deviceType,
          MediaDeviceStateType deviceState) {
        log('onAudioDeviceStateChanged: deviceType:${deviceType} deviceState:${deviceState.name}');
      },
      // onLocalAudioStateChanged: (RtcConnection connection, LocalAudioStreamState state,
      //     LocalAudioStreamError error){
      //   log('onLocalAudioStateChanged: state:${state.name} error:${error.name}');
      // },
      onLocalAudioStateChanged: (connection, state, reason) {
        log('onLocalAudioStateChanged: state:${state.name}');
      },
      onAudioDeviceVolumeChanged:
          (MediaDeviceType deviceType, int volume, bool muted) {
        log('onAudioDeviceVolumeChanged: deviceType:${deviceType.name} volume:${volume} muted:${muted}');
      },
      onAudioPublishStateChanged: (String channel, StreamPublishState oldState,
          StreamPublishState newState, int elapseSinceLastState) {
        log('onAudioPublishStateChanged: oldState:${oldState.name} newState:${newState} elapseSinceLastState:${elapseSinceLastState}');
      },
      onClientRoleChangeFailed: (RtcConnection connection,
          ClientRoleChangeFailedReason reason, ClientRoleType currentRole) {
        if (role != null) {
          _engine.setClientRole(role: role!);
          log('onClientRoleChangeFailed: reason:${reason} currentRole:${currentRole} latestRoleChangeTo:${role}');
        }
      },
    );

    _engine.registerEventHandler(_handler!);
  }

  /// 初始化musiccontentcenter
  Future<void> _initMusicContentCenter(String rtmToken, int id) async {
    // _musicContentCenter = rtcEngine.getMusicContentCenter();
    // MusicContentCenterConfiguration configuration =
    //     MusicContentCenterConfiguration(
    //   appId: RtcManager.appId,
    //   token: rtmToken,
    //   mccUid: id,
    // );
    // await _musicContentCenter.initialize(configuration);
  }

  Future<void> changeRoleToAudience() async {
    log('Change role to audience with current role: $role');

    role = ClientRoleType.clientRoleAudience;
    _engine.setClientRole(
        role: ClientRoleType.clientRoleAudience,
        options: ClientRoleOptions(audienceLatencyLevel: latencyType));
  }

  Future<void> changeRoleToBroadcaster() async {
    log('Change role to broadcaster with current role: $role');

    role = ClientRoleType.clientRoleBroadcaster;
    _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
  }

  joinChannel(String token, String channelId, int uid) async {
    log('Join channel with token: $token, channelId: $channelId, uid: $uid, joined: $_joined');

    // if (_joined) return;

    role = ClientRoleType.clientRoleAudience;

    // 加入频道
    await _engine.joinChannel(
        token: token,
        channelId: channelId,
        uid: uid,
        options: ChannelMediaOptions(
          channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
          clientRoleType: role,
          autoSubscribeAudio: true,
          autoSubscribeVideo: false,
          publishMicrophoneTrack: true,
          publishCameraTrack: false,
          publishMediaPlayerAudioTrack: true,
          publishMediaPlayerVideoTrack: false,
          enableAudioRecordingOrPlayout: true,
          publishMediaPlayerId: musicPlayerId,
          audienceLatencyLevel: latencyType,
        ));

    _joined = true;
  }

  AudienceLatencyLevelType get latencyType => _lowLatency
      ? AudienceLatencyLevelType.audienceLatencyLevelLowLatency
      : AudienceLatencyLevelType.audienceLatencyLevelUltraLowLatency;

  void updateMediaOptions(ChannelMediaOptions mediaOptions) {
    if (_inited && _joined) {
      _engine.updateChannelMediaOptions(mediaOptions);
    }
  }

  Future<void> leaveChannel() async {
    RLog.i('$runtimeType leaveChannel');
    await _dispose();
  }

  Future<void> _dispose() async {
    _joined = false;
    _inited = false;
    role = ClientRoleType.clientRoleAudience;

    _engine.unregisterEventHandler(_handler!);

    try {
      // await _musicContentCenter.release();
    } on Exception catch (e) {}

    try {
      await _engine.leaveChannel();
    } on Exception catch (e) {}

    try {
      await _engine.release();
    } on Exception catch (e) {}
  }

  Future<void> openMic() async {
    log('openMic with role: $role');

    if (role != ClientRoleType.clientRoleBroadcaster) {
      await changeRoleToBroadcaster();
    }

    _engine.muteLocalAudioStream(false);
  }

  Future<void> closeMic() async {
    log('openMic with role: $role');
    _engine.muteLocalAudioStream(true);
  }

  Future<void> startPlayAudio(
      String filePath, bool loopback, int cycle, int startPos) async {
    log('startPlayAudio with filePath: $filePath, loopback: $loopback, cycle: $cycle, startPos: $startPos');
    _engine.startAudioMixing(
        filePath: filePath,
        loopback: loopback,
        cycle: cycle,
        startPos: startPos);
  }

  log(String msg) => RLog.d('RtcManager: $msg');
}
